home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
ohlutil.zip
/
CREATE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-31
|
14KB
|
516 lines
/* create -- make new files
Copyright (C) 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Usage: create [-dfnpqtP] [-m mode] [-x prefix] [+directory] [+fifo] [+pipe]
[+mode mode] [+no-create] [+path] [+quiet] [+silent] [+temporary]
[+prefix prefix] [+portability] file...
Construct a valid pathname including each FILE name, make sure that
file does not already exist, and create an empty file with that name.
Validation consists of making sure that all directories in the
constructed pathname exist and have x permission and whether
strlen (constructed_pathname) <= PATH_MAX
&& strlen (each_directory_in_path) <= NAME_MAX
Default permissions for created files are rw-rw-rw- - umask.
The created files' names are constructed from:
[directory/] from -t (TMPDIR or /tmp)
[prefix] from -x
file command line args
[suffix] from -t
Exit status:
0 Valid pathnames constructed and (unless -n given)
files created.
1 Length of constructed pathname or component
exceeds limit.
2 If -P, constructed pathname contains characters
not in the portable filename character set.
3 Constructed pathname existed and -p not given;
or -t given and could not construct nonexisting
pathname.
>3 -n not given and couldn't create file because of
permissions, file system full, etc.
All errors are fatal.
Options:
-d, +directory Make a directory instead of a regular file.
Default permissions are rwxrwxrwx - umask.
-f, +fifo, +pipe Make a fifo instead of a regular file.
-m, +mode mode Set the mode of created files to MODE, which
is symbolic as in chmod and uses the umask as a
point of departure.
-n, +no-create Don't check for the file's existence or try to
create it.
-p, +path Do not check whether the path prefix or the file
to be created exist.
An error occurs if any constructed pathname
already exists and has a different type than the
file to create. Otherwise, if -m is given,
set the file's permission modes.
Make any missing parent directories for each argument.
Parent dirs default to umask modified by `u+wx'.
-q, +quiet, +silent When given with -t or -x, do not write pathname.
-t, +temporary Append a SUFFIX of chars in the portable filename
character set to the FILE name such that the
resulting pathname is unique and still valid.
If TMPDIR is set in the environment and neither
FILE nor PREFIX contains a slash, use the
value of TMPDIR as the directory pathname;
otherwise, use /tmp.
If -m is not given, do go= on the default mode.
Write the constructed pathname to stdout.
-x, +prefix prefix Use PREFIX when constructing path names.
Write the constructed pathname to stdout.
-P, +portability Instead of performing length checks on the
underlying filesystem, test the length of the
pathname and its components against the POSIX.1
minimum limits for portability, _POSIX_NAME_MAX
and _POSIX_PATH_MAX in 2.9.2. Also check that
the pathname contains no characters not in the
portable filename character set.
David MacKenzie <djm@ai.mit.edu> */
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "getopt.h"
#include "modechange.h"
#ifdef STDC_HEADERS
#include <errno.h>
#include <stdlib.h>
#else
char *getenv ();
char *malloc ();
extern int errno;
#endif
#ifndef _POSIX_SOURCE
#define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
#endif
int mktemp ();
char *construct_path ();
char *xmalloc ();
void ensure_path_exists ();
void error ();
void strip_trailing_slashes ();
void usage ();
void validate_new_path ();
/* The name this program was run with. */
char *program_name;
struct option longopts[] =
{
{"directory", 0, NULL, 'd'},
{"fifo", 0, NULL, 'f'},
{"pipe", 0, NULL, 'f'},
{"mode", 1, NULL, 'm'},
{"no-create", 0, NULL, 'n'},
{"path", 0, NULL, 'p'},
{"quiet", 0, NULL, 'q'},
{"silent", 0, NULL, 'q'},
{"temporary", 0, NULL, 't'},
{"prefix", 1, NULL, 'x'},
{"portability", 0, NULL, 'P'},
{NULL, 0, NULL, 0}
};
void
main (argc, argv)
int argc;
char **argv;
{
unsigned filetype; /* The type of file to create. */
int make_missing_parents; /* If nonzero, ensure that a path exists. */
int create_file; /* If nonzero, try to create the file. */
int echo_pathnames; /* If nonzero, write pathnames to stdout. */
int make_temporary; /* If nonzero, make file in a temp dir. */
int check_portability; /* If nonzero, check POSIX portability. */
char *tmpdir; /* Value of TMPDIR variable or "/tmp". */
int tmpdir_length; /* Length of `tmpdir' + '/'. */
unsigned short newmode;
unsigned short parent_mode;
struct mode_change *change;
char *symbolic_mode;
char *prefix;
int prefix_length;
int silent;
int optc;
int ind;
program_name = argv[0];
filetype = S_IFREG;
make_missing_parents = 0;
create_file = 1;
echo_pathnames = 0;
make_temporary = 0;
check_portability = 0;
tmpdir = prefix = "";
tmpdir_length = 0;
prefix_length = 0;
symbolic_mode = NULL;
silent = 0;
while ((optc = getopt_long (argc, argv, "dfnpqtPm:x:", longopts, &ind))
!= EOF)
{
if (optc == 0 && longopts[ind].flag == 0)
optc = longopts[ind].val;
switch (optc)
{
case 0: /* Long option. */
break;
case 'd':
filetype = S_IFDIR;
break;
case 'f':
#ifdef S_IFIFO
filetype = S_IFIFO;
#else
error (4, 0, "fifo files are not supported on this system");
#endif
break;
case 'm':
symbolic_mode = optarg;
break;
case 'n':
create_file = 0;
break;
case 'p':
make_missing_parents = 1;
break;
case 'q':
silent = 1;
break;
case 't':
make_temporary = 1;
echo_pathnames = 1;
break;
case 'x':
prefix = optarg;
prefix_length = strlen (prefix);
echo_pathnames = 1;
break;
case 'P':
check_portability = 1;
break;
default:
usage ();
}
}
if (optind == argc)
usage ();
if (silent)
echo_pathnames = 0;
newmode = 0777 & ~umask (0);
parent_mode = newmode | 0300; /* u+wx */
if (filetype != S_IFDIR)
newmode &= ~0111;
if (symbolic_mode)
{
change = mode_compile (symbolic_mode, MODE_MASK_EQUALS | MODE_MASK_PLUS);
if (change == MODE_INVALID)
error (4, 0, "invalid mode");
else if (change == MODE_MEMORY_EXHAUSTED)
error (4, 0, "virtual memory exhausted");
newmode = mode_adjust (newmode, change);
}
else if (make_temporary)
newmode &= ~0077; /* go= */
if (make_temporary || prefix)
{
tmpdir = getenv ("TMPDIR");
if (tmpdir)
tmpdir_length = strlen (tmpdir) + 1;
}
for (; optind < argc; ++optind)
{
char *file = argv[optind];
strip_trailing_slashes (file);
if (make_temporary || prefix)
file = construct_path (file, make_temporary, tmpdir, tmpdir_length,
prefix, prefix_length);
if (echo_pathnames)
puts (file);
if (make_missing_parents)
ensure_path_exists (file, filetype, newmode, parent_mode);
else
validate_new_path (file, check_portability);
if (create_file)
{
switch (filetype)
{
case S_IFREG:
ind = open (file, O_WRONLY | O_CREAT | O_EXCL, newmode);
if (ind == -1)
error (4, errno, "%s", file);
else
close (ind);
break;
#ifdef S_IFIFO
case S_IFIFO:
if (mkfifo (file, newmode))
error (4, errno, "%s", file);
break;
#endif
case S_IFDIR:
if (mkdir (file, newmode))
error (4, errno, "%s", file);
break;
}
}
if (file != argv[optind])
free (file);
}
exit (0);
}
/* Return a path with the form:
[directory/] TMPDIR or /tmp if MAKE_TEMPORARY, else empty
[prefix] PREFIX if set, else empty
file BASE
[suffix] letter+pid if MAKE_TEMPORARY, else empty */
char *
construct_path (base, make_temporary, tmpdir, tmpdir_length,
prefix, prefix_length)
char *base;
int make_temporary;
char *tmpdir;
int tmpdir_length;
char *prefix;
int prefix_length;
{
char *path, *p;
int base_length;
base_length = strlen (base);
if (make_temporary)
{
if (tmpdir == NULL || index (base, '/') || index (prefix, '/'))
{
tmpdir = "/tmp";
tmpdir_length = 5; /* Count a trailing slash. */
}
}
path = xmalloc (tmpdir_length + prefix_length + base_length
+ (make_temporary ? 6 : 0) + 1);
p = path;
if (make_temporary)
{
strcpy (p, tmpdir);
p += tmpdir_length - 1;
*p++ = '/';
}
strcpy (p, prefix);
p += prefix_length;
strcpy (p, base);
if (make_temporary)
{
p += base_length;
strcpy (p, "XXXXXX");
mktemp (path);
}
return path;
}
/* Make sure file PATH, which has type PATHTYPE,
and all leading directories exist,
and give it permission mode MODE.
If any leading directories are created, give them permission
mode PARENT_MODE.
Exit if an error occurs. */
void
ensure_path_exists (path, pathtype, mode, parent_mode)
char *path;
unsigned pathtype;
unsigned short mode;
unsigned short parent_mode;
{
char *slash;
struct stat stats;
if (stat (path, &stats))
{
slash = path;
while (slash = index (slash, '/'))
{
*slash = 0;
if (stat (path, &stats))
{
if (mkdir (path, parent_mode))
error (4, errno, "cannot make directory `%s'", path);
}
else if ((stats.st_mode & S_IFMT) != S_IFDIR)
error (4, 0, "`%s' is not a directory", path);
*slash++ = '/';
}
}
else if ((stats.st_mode & S_IFMT) != pathtype)
error (4, 0, "`%s' has the wrong type", path);
else if (chmod (path, mode))
error (4, errno, "cannot change mode of `%s'", path);
}
/* Each element is nonzero if the corresponding ASCII character is
in the POSIX portable character set, and zero if it is not.
In addition, the entry for `/' is nonzero to simplify checking. */
char portable_chars[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* Make sure that PATH does not exist, but that all directories along
the way do exist and have `x' permission, and that
strlen (PATH) <= PATH_MAX
&& strlen (each-directory-in-PATH) <= NAME_MAX
If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
_POSIX_NAME_MAX instead, and make sure that PATH contains no
characters not in the POSIX portable filename character set, which
consists of A-Z, a-z, 0-9, ., _, -.
Exit if an error occurs. */
void
validate_new_path (path, portability)
char *path;
int portability;
{
char *start, *end;
int name_max, path_max;
struct stat stats;
/* Perform the cheaper tests first. */
path_max = portability ? _POSIX_PATH_MAX : PATH_MAX;
name_max = portability ? _POSIX_NAME_MAX : NAME_MAX;
if (strlen (path) > path_max)
error (1, 0, "path `%s' exceeds length limit", path);
if (portability)
{
for (start = path; *start; ++start)
if (portable_chars[*start] == 0)
error (2, 0, "path `%s' contains nonportable character `%c'",
path, *start);
}
if (stat (path, &stats) == 0)
error (3, 0, "`%s' already exists", path);
start = path;
do
{
end = index (start + 1, '/');
if (end == NULL)
end = index (start + 1, 0);
if (end - start - (*start == '/') > name_max)
error (1, 0, "name `%s' exceeds length limit", path);
if (*end == '/')
{
*end = 0;
if (stat (path, &stats) == -1)
error (4, errno, "%s", path);
else if ((stats.st_mode & S_IFMT) != S_IFDIR)
error (4, 0, "`%s' is not a directory", path);
*end = '/';
}
start = end;
}
while (*start);
}
/* Remove trailing slashes from PATH; they cause some system calls to fail. */
void
strip_trailing_slashes (path)
char *path;
{
int last;
last = strlen (path) - 1;
while (last > 0 && path[last] == '/')
path[last--] = '\0';
}
/* Allocate N bytes of memory dynamically, with error checking. */
char *
xmalloc (n)
unsigned n;
{
char *p;
p = malloc (n);
if (p == 0)
error (4, 0, "virtual memory exhausted");
return p;
}
void
usage ()
{
fprintf (stderr, "\
Usage: %s [-dfnpqtP] [-m mode] [-x prefix] [+directory] [+fifo] [+pipe]\n\
[+mode mode] [+no-create] [+path] [+quiet] [+silent] [+temporary]\n\
[+prefix prefix] [+portability] file...\n",
program_name);
exit (1);
}